/*
 *  UVCCamera
 *  library and sample to access to UVC web camera on non-rooted Android device
 *
 * Copyright (c) 2014-2017 saki t_saki@serenegiant.com
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 *
 *  All files in the folder are under this Apache License, Version 2.0.
 *  Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
 *  may have a different license, see the respective files.
 */

package com.serenegiant.usbcameratest0;

import android.hardware.usb.UsbDevice;
import android.os.Bundle;
import android.util.Log;
import android.util.SparseArray;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageButton;
import android.widget.Toast;

import com.serenegiant.common.BaseActivity;
import com.serenegiant.usb.CameraDialog;
import com.serenegiant.usb.DeviceFilter;
import com.serenegiant.usb.USBMonitor;
import com.serenegiant.usb.USBMonitor.OnDeviceConnectListener;
import com.serenegiant.usb.USBMonitor.UsbControlBlock;
import com.serenegiant.usb.UVCCamera;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class MainActivity extends BaseActivity implements CameraDialog.CameraDialogParent {
    private static final boolean DEBUG = true;    // TODO set false when production
    private static final String TAG = "MainActivity";

    private final Object mSync = new Object();
    // for accessing USB and USB camera
    private USBMonitor mUSBMonitor;
    // private UVCCamera mUVCCamera;
    // private SurfaceView mUVCCameraView;
    // for open&start / stop&close camera preview
    private ImageButton mCameraButton;
    // private Surface mPreviewSurface;
    private boolean isActive, isPreview;
    private SparseArray<UVCCameraSession> sessionArray = new SparseArray<>();
    Map<SurfaceView, Boolean> surfaceViewMap = new HashMap<>();

    @Override
    protected void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mCameraButton = (ImageButton) findViewById(R.id.camera_button);
        mCameraButton.setOnClickListener(mOnClickListener);

        SurfaceView mUVCCameraView = (SurfaceView) findViewById(R.id.camera_surface_view);
        mUVCCameraView.getHolder().addCallback(mSurfaceViewCallback);
        surfaceViewMap.put(mUVCCameraView, false);
        SurfaceView surfaceView = (SurfaceView) findViewById(R.id.camera_surface_view2);
        surfaceView.getHolder().addCallback(mSurfaceViewCallback);
        surfaceViewMap.put(surfaceView, false);

        mUSBMonitor = new USBMonitor(this, mOnDeviceConnectListener);

        try {

            // ReflectUtils.getReflectField()
        } catch (Exception e) {

        }
    }

    @Override
    protected void onStart() {
        super.onStart();
        if (DEBUG) Log.v(TAG, "onStart:");
        synchronized (mSync) {
            if (mUSBMonitor != null) {
                mUSBMonitor.register();
            }
        }
    }

    @Override
    protected void onStop() {
        if (DEBUG) Log.v(TAG, "onStop:");
        synchronized (mSync) {
            if (mUSBMonitor != null) {
                mUSBMonitor.unregister();
            }
        }
        super.onStop();
    }

    @Override
    protected void onDestroy() {
        Log.e(TAG, "onDestroy: ");
        if (DEBUG) Log.v(TAG, "onDestroy:");
        // synchronized (mSync) {
        int size = sessionArray.size();
        Log.e(TAG, "onDestroy: " + size);
        for (int i = 0; i < size; i++) {
            UVCCameraSession session = sessionArray.get(sessionArray.keyAt(i));

            session.isActive = session.isPreview = false;
            if (session.uvcCamera != null) {
                Log.e(TAG, "onDestroy: before destory");
                session.uvcCamera.destroy();
                Log.e(TAG, "onDestroy: after destory");
                session.uvcCamera = null;
            }

        }
        // }
        if (mUSBMonitor != null) {
            mUSBMonitor.destroy();
            mUSBMonitor = null;
        }
        // mUVCCameraView = null;
        mCameraButton = null;
        super.onDestroy();
    }

    private final OnClickListener mOnClickListener = new OnClickListener() {
        @Override
        public void onClick(final View view) {
            CameraDialog.showDialog(MainActivity.this);
            final List<DeviceFilter> filter = DeviceFilter.getDeviceFilters(getApplicationContext(), com.serenegiant.uvccamera.R.xml.device_filter);
            for (DeviceFilter deviceFilter : filter) {
                Log.e(TAG, "getDeviceFilters: " + deviceFilter);
            }
        }
    };

    private final OnDeviceConnectListener mOnDeviceConnectListener = new OnDeviceConnectListener() {
        @Override
        public void onAttach(final UsbDevice device) {
            if (DEBUG) Log.v(TAG, "onAttach:");
            Toast.makeText(MainActivity.this, "USB_DEVICE_ATTACHED", Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onConnect(final UsbDevice device, final UsbControlBlock ctrlBlock, final boolean createNew) {
            if (DEBUG) Log.v(TAG, "onConnect:");
            synchronized (mSync) {
                UVCCameraSession uvcCameraSession = sessionArray.get(device.getDeviceId());
                if (uvcCameraSession != null) {
                    Toast.makeText(getApplicationContext(), "摄像头已打开!", Toast.LENGTH_LONG).show();
                    return;
                }
            }
            queueEvent(new Runnable() {
                @Override
                public void run() {
                    synchronized (mSync) {
                        final UVCCamera camera = new UVCCamera();
                        camera.open(ctrlBlock);
                        if (DEBUG) Log.i(TAG, "supportedSize:" + camera.getSupportedSize());
                        try {
                            camera.setPreviewSize(UVCCamera.DEFAULT_PREVIEW_WIDTH, UVCCamera.DEFAULT_PREVIEW_HEIGHT, UVCCamera.FRAME_FORMAT_MJPEG);
                        } catch (final IllegalArgumentException e) {
                            try {
                                // fallback to YUV mode
                                camera.setPreviewSize(UVCCamera.DEFAULT_PREVIEW_WIDTH, UVCCamera.DEFAULT_PREVIEW_HEIGHT, UVCCamera.DEFAULT_PREVIEW_MODE);
                            } catch (final IllegalArgumentException e1) {
                                camera.destroy();
                                return;
                            }
                        }
                        UVCCameraSession session = new UVCCameraSession(device, getApplicationContext());

                        for (Map.Entry<SurfaceView, Boolean> entry : surfaceViewMap.entrySet()) {
                            if (!entry.getValue()) {
                                session.previewSurface = entry.getKey().getHolder().getSurface();
                                surfaceViewMap.put(entry.getKey(), true);
                                session.surfaceView = entry.getKey();
                                break;
                            }
                        }
                        if (session.previewSurface != null) {
                            session.isActive = true;
                            camera.setPreviewDisplay(session.previewSurface);
                            camera.startPreview();
                            session.isPreview = true;
                        }
                        synchronized (mSync) {
                            session.uvcCamera = camera;
                            // camera.setFrameCallback(session, UVCCamera.PIXEL_FORMAT_NV21);

                            sessionArray.put(device.getDeviceId(), session);
                            Log.e(TAG, "run: put2SessionArray " + sessionArray.size());
                            int rotation = CameraHelper.getCameraDisplayRotation(getApplicationContext(), 0);
                            int rotation1 = CameraHelper.getCameraDisplayRotation(getApplicationContext(), 1);
                            Log.d(TAG, "run:rotation " + rotation + ", " + rotation1);
                        }
                    }
                }
            }, 0);
        }

        @Override
        public void onDisconnect(final UsbDevice device, final UsbControlBlock ctrlBlock) {
            if (DEBUG) Log.v(TAG, "onDisconnect:");
            // XXX you should check whether the comming device equal to camera device that currently using
            queueEvent(new Runnable() {
                @Override
                public void run() {
                    synchronized (mSync) {
                        UVCCameraSession session = sessionArray.get(device.getDeviceId());
                        if (session != null) {
                            if (session.uvcCamera != null) {
                                session.uvcCamera.close();
                                if (session.previewSurface != null) {
                                    session.previewSurface.release();
                                    session.previewSurface = null;
                                }
                                session.isActive = session.isPreview = false;
                            }
                            sessionArray.remove(device.getDeviceId());
                            surfaceViewMap.put(session.surfaceView, false);
                        }
                    }
                }
            }, 0);
        }

        @Override
        public void onDettach(final UsbDevice device) {
            if (DEBUG) Log.v(TAG, "onDettach:");
            Toast.makeText(MainActivity.this, "USB_DEVICE_DETACHED", Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onCancel(final UsbDevice device) {
        }
    };

    /**
     * to access from CameraDialog
     *
     * @return
     */
    @Override
    public USBMonitor getUSBMonitor() {
        return mUSBMonitor;
    }

    @Override
    public void onDialogResult(boolean canceled) {
        if (canceled) {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    // FIXME
                }
            }, 0);
        }
    }

    private final SurfaceHolder.Callback mSurfaceViewCallback = new SurfaceHolder.Callback() {
        @Override
        public void surfaceCreated(final SurfaceHolder holder) {
            if (DEBUG) Log.v(TAG, "surfaceCreated:");

        }

        @Override
        public void surfaceChanged(final SurfaceHolder holder, final int format, final int width, final int height) {
            if ((width == 0) || (height == 0)) return;
            if (DEBUG) Log.v(TAG, "surfaceChanged:");
            int size = sessionArray.size();
            Surface surface = holder.getSurface();
            synchronized (mSync) {
                for (int i = 0; i < size; i++) {
                    UVCCameraSession session = sessionArray.get(sessionArray.keyAt(i));
                    if (session.previewSurface == surface) {
                        if (session.isActive && !session.isPreview && (session.uvcCamera != null)) {
                            session.uvcCamera.setPreviewDisplay(surface);
                            session.uvcCamera.startPreview();
                            session.isPreview = true;
                        }
                    }
                }
            }
        }

        @Override
        public void surfaceDestroyed(final SurfaceHolder holder) {
            if (DEBUG) Log.v(TAG, "surfaceDestroyed:");
            synchronized (mSync) {
                int size = sessionArray.size();
                Surface surface = holder.getSurface();
                for (int i = 0; i < size; i++) {
                    UVCCameraSession session = sessionArray.get(sessionArray.keyAt(i));
                    Log.e(TAG, "surfaceDestroyed: " + session.previewSurface + ", " + surface);
                    // if (session != null && session.previewSurface == surface) {
                    if (session.uvcCamera != null) {
                        session.uvcCamera.stopPreview();
                        session.uvcCamera.setFrameCallback(null, 0);
                        session.uvcCamera.destroy();
                    }
                    session.previewSurface = null;
                    session.isPreview = false;
                    // }
                    sessionArray.remove(session.usbDevice.getDeviceId());
                    surfaceViewMap.put(session.surfaceView, false);
                    Log.e(TAG, "surfaceDestroyed: " + sessionArray.size());
                }
            }
        }
    };

    public void androidCamera(View view) {
        finish();

    }


    public void exit(View view) {
        System.exit(0);
    }
}
